package com.itasura.guava.Concurrency.mointor;

import com.google.common.util.concurrent.Monitor;
import com.itasura.guava.bean.City;
import lombok.extern.slf4j.Slf4j;

/**
 * Monitor 版
 * <p>
 * Monitor有几个常用的方法
 * </p>
 * enter()：进入到当前Monitor，无限期阻塞，等待锁。
 * enter(long time, TimeUnit unit)：进入到当前Monitor，最多阻塞给定的时间，返回是否进入Monitor。
 * tryEnter()：如果可以的话立即进入Monitor，不阻塞，返回是否进入Monitor。
 * enterWhen(Guard guard)：进入当前Monitor，等待Guard的isSatisfied()为true后，继续往下执行 ，但可能会被打断。
 * enterIf(Guard guard)：如果Guard的isSatisfied()为true，进入当前Monitor。等待获得锁，不需要等待Guard satisfied。
 * tryEnterIf(Guard guard)：如果Guard的isSatisfied()为true并且可以的话立即进入Monitor，不等待获取锁，也不等待Guard satisfied。
 *
 * @author sailor wang
 * @date 2018/10/30 11:17 AM
 * @description
 */
@Slf4j
public class SafeBoxV3<V> {

    private final Monitor monitor = new Monitor();
    private final Monitor.Guard valuePresent = new Monitor.Guard(monitor) {
        @Override
        public boolean isSatisfied() {
            return value != null;
        }
    };

    private final Monitor.Guard valueAbsent = new Monitor.Guard(monitor) {
        @Override
        public boolean isSatisfied() {
            return value == null;
        }
    };

    private V value;

    public V get() throws InterruptedException {
        try {
            monitor.enterWhen(valuePresent);
            V result = value;
            value = null;
            return result;
        } finally {
            monitor.leave();
        }
    }

    public void set(V newValue) throws InterruptedException {
        try {
            monitor.enterWhen(valueAbsent);
            value = newValue;
        } finally {
            monitor.leave();
        }
    }


    public static void main(String[] args) throws InterruptedException {


        SafeBoxV3<City> safeBox = new SafeBoxV3<City>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    log.info("开始获取值......");
                    City c = safeBox.get();
                    log.info("获取完成，值 -> {}", c);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(1000);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    City city = new City();
                    city.setName("上海");
                    log.info("开始设置值......");
                    safeBox.set(city);
                    log.info("设置完成......");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}